/***************************************************************************
 * Copyright (C) 2012
 * by Dj_Skual
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * SearchWindow.cpp
 *
 * for SaveGame Manager GX 2012
 ***************************************************************************/
#include "SearchWindow.h"
#include "../Themes/CTheme.h"
#include "../Controls/Application.h"
#include "../Menu/TaskBar.hpp"
#include "../Menu/Explorer.h"

/**
 *Class to display chars buttons
 */
cSearchButton::cSearchButton(std::wstring Char, const char *keyImageData, const char *keyOverImageData, int x, int y, GuiTrigger* trig, GuiSound* sndClick, GuiSound* sndOver)
	: PictureButton(keyImageData, keyOverImageData, sndClick, sndOver)
{
	wChar = Char;
	text = new GuiText(Char.c_str(), thInt("20 - search buttons text size"), thColor("r=0 g=0 b=0 a=255 - search buttons text color")),
	
	SetPosition(x, y);
	SetLabel(text);
	SetTrigger(trig);
}

cSearchButton::~cSearchButton()
{
	if(text)
		delete text;
}

/**
 *Class to display the window
 */
SearchWindow * SearchWindow::instance = NULL;

SearchWindow::SearchWindow()
	: GuiFrame(0, 0)
{
	SearchChars.clear();
	CurrentFilter.clear();
	
	Pointed = NULL;
	
	Text = NULL;
	ModeText = NULL;
	
	BgImg = NULL;
	TextBarImg = NULL;
	
	ImgBacspaceBtn = NULL;
	BacspaceBtnImg = NULL;
	BacspaceBtnImg_Over = NULL;
	BacspaceBtn = NULL;

	ImgClearBtn = NULL;
	ClearBtnImg = NULL;
	ClearBtnImg_Over = NULL;
	ClearBtn = NULL;

	CloseBtn = NULL;
	SearchModeBtn = NULL;
	
	BtnSoundClick = NULL;
	BtnSoundOver = NULL;
	
	TrigA = NULL;
	TrigB = NULL;
	
	Buttons.clear();
	
	ExitRequested = false;
	WindowClosed = true;
	RefreshNeeded = false;
	InternalRefresh = false;
	
	SetAlignment(thAlign("center - search window align hor") | thAlign("middle - search window align ver"));
	SetPosition(thInt("0 - search window pos x"), thInt("0 - search window pos y"));
	
	this->SetVisible(false);
	
}

SearchWindow::~SearchWindow()
{
	ExitRequested = true;

	Hide();
}

void SearchWindow::Show()
{
	Application::Instance()->Remove(this);
	InternalSetup();
	SetEffect(EFFECT_FADE, 20);
	Application::Instance()->Append(this);
	
	WindowClosed = false;
}

void SearchWindow::InternalSetup()
{
	TrigA = new SimpleGuiTrigger(-1, WiiControls.ClickButton | ClassicControls.ClickButton << 16, GCControls.ClickButton);
	TrigB = new GuiTrigger();
	TrigB->SetButtonOnlyTrigger(-1, WiiControls.BackButton | ClassicControls.BackButton << 16, GCControls.BackButton);

	BtnSoundClick = Resources::GetSound("button_click.wav");
	BtnSoundOver = Resources::GetSound("button_over.wav");
	BtnSoundClick->SetVolume(Settings.SFXVolume);
	BtnSoundOver->SetVolume(Settings.SFXVolume);
	
	SetCharsButtons();
	
	BgImg = new GuiImage(width, height, thColor("r=0 g=0 b=0 a=160 - search window color"));
	Append(BgImg);
	
	TextBarImg = new GuiImage(width - (10 + 3 * 45 + 10), 24, thColor("r=255 g=255 b=255 a=255 - search bar color"));
	TextBarImg->SetPosition(55, 15);
	Append(TextBarImg);
	
	Text = new GuiText(CurrentFilter.c_str(), thInt("22 - search bar text size"), thColor("r=0 g=0 b=0 a=255 - search bar text color"));
	Text->SetPosition(60, 16);
	Text->SetAlignment(ALIGN_LEFT | ALIGN_TOP);
	Text->SetMaxWidth(TextBarImg->GetWidth()-10, SCROLL_HORIZONTAL);
	Append(Text);
	
	ModeText = new GuiText(Settings.SearchMode == SEARCH_BEGINNING ? "=" : "*", 20, thColor("r=0 g=0 b=0 a=255 - search buttons text color"));
	SearchModeBtn = new PictureButton("keyboard_key.png", "keyboard_key_over.png", BtnSoundClick, BtnSoundOver);
	SearchModeBtn->SetLabel(ModeText);
	SearchModeBtn->SetPosition(10, 10);
	SearchModeBtn->SetTrigger(TrigA);
	SearchModeBtn->Clicked.connect(this, &SearchWindow::OnSearchModeButtonClick);
	Append(SearchModeBtn);
	
	ImgBacspaceBtn = Resources::GetImageData("keyboard_backspace_over.png");
	BacspaceBtnImg_Over = new GuiImage(ImgBacspaceBtn);
	BacspaceBtnImg = new GuiImage(ImgBacspaceBtn);
	BacspaceBtnImg->SetGrayscale();
	
	BacspaceBtn = new GuiButton(BacspaceBtnImg->GetWidth(), BacspaceBtnImg_Over->GetHeight());
	BacspaceBtn->SetAlignment(ALIGN_RIGHT | ALIGN_TOP);
	BacspaceBtn->SetPosition(-55, 10);
	BacspaceBtn->SetImage(BacspaceBtnImg);
	BacspaceBtn->SetImageOver(BacspaceBtnImg_Over);
	BacspaceBtn->SetSoundOver(BtnSoundOver);
	BacspaceBtn->SetSoundClick(BtnSoundClick);
	BacspaceBtn->SetTrigger(TrigA);
	BacspaceBtn->SetEffectGrow();
	BacspaceBtn->Clicked.connect(this, &SearchWindow::OnBacspaceButtonClick);
	Append(BacspaceBtn);
	
	ImgClearBtn = Resources::GetImageData("keyboard_clear_over.png");
	ClearBtnImg_Over = new GuiImage(ImgClearBtn);
	ClearBtnImg = new GuiImage(ImgClearBtn);
	ClearBtnImg->SetGrayscale();
	
	ClearBtn = new GuiButton(ClearBtnImg->GetWidth(), ClearBtnImg_Over->GetHeight());
	ClearBtn->SetAlignment(ALIGN_RIGHT | ALIGN_TOP);
	ClearBtn->SetPosition(-10, 10);
	ClearBtn->SetImage(ClearBtnImg);
	ClearBtn->SetImageOver(ClearBtnImg_Over);
	ClearBtn->SetSoundOver(BtnSoundOver);
	ClearBtn->SetSoundClick(BtnSoundClick);
	ClearBtn->SetTrigger(TrigA);
	ClearBtn->SetEffectGrow();
	ClearBtn->Clicked.connect(this, &SearchWindow::OnClearButtonClick);
	Append(ClearBtn);
	
	CloseBtn = new GuiButton(0, 0);
	CloseBtn->SetTrigger(TrigB);
	CloseBtn->Clicked.connect(this, &SearchWindow::OnCloseButtonClick);
	Append(CloseBtn);
	
	Pointed = new GuiPointed(width, height);
	Pointed->StateChanged.connect(this, &SearchWindow::OnStateChange);
	Append(Pointed);
	
	for(u32 i = 0; i < Buttons.size(); i++)
		Append(Buttons[i]);
	
	this->SetVisible(true);
}

void SearchWindow::Hide()
{
	if(!Application::isClosing())
	{
		if(!ExitRequested && parentElement)
		{
			SetEffect(EFFECT_FADE, -20);
			while(IsAnimated())
				Application::Instance()->updateEvents();
		}
		
		Application::Instance()->UnsetUpdateOnly(this);
	}
	
	this->DimBackground(false);

	InternalHide();
	
	WindowClosed = true;
}

void SearchWindow::InternalHide()
{
	this->SetVisible(false);
	
	RemoveAll();

	SearchChars.clear();
	CurrentFilter.clear();
	
	if(Pointed)
		delete Pointed;
		
	if(Text)
		delete Text;
	if(ModeText)
		delete ModeText;
	
	if(TrigA)
		delete TrigA;
	if(TrigB)
		delete TrigB;

	Resources::Remove(BtnSoundOver);
	Resources::Remove(BtnSoundClick);

	if(CloseBtn)
		delete CloseBtn;
	if(SearchModeBtn)
		delete SearchModeBtn;
	
	Resources::Remove(ImgBacspaceBtn);
	if(BacspaceBtnImg)
		delete BacspaceBtnImg;
	if(BacspaceBtnImg_Over)
		delete BacspaceBtnImg_Over;
	if(BacspaceBtn)
		delete BacspaceBtn;
	
	Resources::Remove(ImgClearBtn);
	if(ClearBtnImg)
		delete ClearBtnImg;
	if(ClearBtnImg_Over)
		delete ClearBtnImg_Over;
	if(ClearBtn)
		delete ClearBtn;
	
	if(BgImg)
		delete BgImg;
	if(TextBarImg)
		delete TextBarImg;
	
	for(int i = 0; i < (int)Buttons.size(); ++i)
		delete Buttons[i];
	
	Buttons.clear();
	
	Text = NULL;
	ModeText = NULL;

	BtnSoundOver = NULL;
	BtnSoundClick = NULL;

	TrigA = NULL;
	TrigB = NULL;

	CloseBtn = NULL;
	SearchModeBtn = NULL;
	
	ImgBacspaceBtn = NULL;
	BacspaceBtnImg = NULL;
	BacspaceBtnImg_Over = NULL;
	BacspaceBtn = NULL;

	ImgClearBtn = NULL;
	ClearBtnImg = NULL;
	ClearBtnImg_Over = NULL;
	ClearBtn = NULL;

	BgImg = NULL;
	TextBarImg = NULL;

	Pointed = NULL;
}

void SearchWindow::Refresh()
{
	RefreshNeeded = false;
	InternalRefresh = false;
	
	InternalHide();
	InternalSetup();
}

void SearchWindow::SetCharsButtons()
{
	CurrentBrowser()->ListChanged.connect(this, &SearchWindow::OnBrowserListChange);
	
	CurrentFilter = CurrentBrowser()->GetCurrentFilter();
	SearchChars = CurrentBrowser()->GetSearchChars();
	
	int cnt = SearchChars.size();
	if(!cnt)
	{
		width = 320;
		height = 62;
		return;
	}
	
	wchar_t charstr[2] = { 0, 0 };
	int lines = (cnt + 9) / 10;
	int buttonsPerLine = (cnt + lines - 1) / lines;
	width = 10 + buttonsPerLine * 42 + 10;
	int i = 0, x_start = 10, x = 0, y_start = 10 + 42, y = 0;
	if (width < 320)
	{
		x_start += (320 - width) >> 1;
		width = 320;
	}
	for (std::set<wchar_t>::iterator it=SearchChars.begin() ; it != SearchChars.end(); it++, i++, x++)
	{
		if (x >= buttonsPerLine) x = 0;
		if (x == 0) y++;
		charstr[0] = *it;
		if(i >= (int)Buttons.size())
			AddCharButton(charstr, x_start+x*42, y_start-42+y*42);
	}
	height = 10 + 42 + y * 42 + 10;
}

void SearchWindow::AddCharButton(std::wstring text, int x, int y)
{
	cSearchButton * btn = new cSearchButton(text, "keyboard_key.png", "keyboard_key_over.png", x, y, TrigA, BtnSoundClick, BtnSoundOver);
	btn->Clicked.connect(this, &SearchWindow::OnCharButtonClick);
	
	Buttons.push_back(btn);
}

void SearchWindow::OnBrowserListChange()
{
	if(!WindowClosed && !RefreshNeeded)
		RefreshNeeded = true;
	
	TaskBar::Instance()->UpdateSearchButton();
}

void SearchWindow::OnStateChange(GuiElement *sender UNUSED, int state, int stateChan UNUSED)
{
	if(state == STATE_SELECTED)
	{
		Application::Instance()->BringToFront(this);
		Application::Instance()->SetUpdateOnly(this);
		DimBackground(true);
	}
	else
	{
		Application::Instance()->UnsetUpdateOnly(this);
		DimBackground(false);
	}
}

void SearchWindow::OnSearchModeButtonClick(GuiButton *sender UNUSED, int pointer UNUSED, const POINT &p UNUSED)
{
	if(++Settings.SearchMode >= SEARCH_MAX)
		Settings.SearchMode = SEARCH_BEGINNING;
	
	InternalRefresh = true;
	CurrentBrowser()->FilterList(CurrentFilter);
}

void SearchWindow::OnBacspaceButtonClick(GuiButton *sender UNUSED, int pointer UNUSED, const POINT &p UNUSED)
{
	RemoveLastChar();
}

void SearchWindow::OnClearButtonClick(GuiButton *sender UNUSED, int pointer UNUSED, const POINT &p UNUSED)
{
	if(!CurrentFilter.empty())
	{
		InternalRefresh = true;
		CurrentBrowser()->FilterList();
	}
}

void SearchWindow::OnCloseButtonClick(GuiButton *sender UNUSED, int pointer UNUSED, const POINT &p UNUSED)
{
	if(Pointed->GetState() == STATE_SELECTED)
		Hide();
	
	TaskBar::Instance()->UpdateSearchButton();
}

void SearchWindow::OnCharButtonClick(GuiButton *sender, int pointer UNUSED, const POINT &p UNUSED)
{
	for (u32 i = 0; i < (int)Buttons.size(); i++)
	{
		if (sender != Buttons[i])
			continue;
		
		AddNewChar(i);
		break;
	}
}

void SearchWindow::AddNewChar(int i)
{
	CurrentFilter += Buttons[i]->wChar;
	
	InternalRefresh = true;
	CurrentBrowser()->FilterList(CurrentFilter);
}

void SearchWindow::RemoveLastChar()
{
	if(!CurrentFilter.empty())
	{
		CurrentFilter.erase(CurrentFilter.end()-1);
		
		InternalRefresh = true;
		CurrentBrowser()->FilterList(CurrentFilter);
	}
}

void SearchWindow::Draw()
{
	if(RefreshNeeded && !InternalRefresh)
		CurrentBrowser()->FilterList();
	
	if(RefreshNeeded)
		Refresh();
	
	GuiFrame::Draw();
}
